#pragma once

#include <cstddef>
#include <vector>
#include <queue>
#include <map>
#include <string>
#include <alc.h>
#include <al.h>
#include "openaudio_global.h"
#include <osg/Vec3>
#include "base.h"
#include <osg\ref_ptr>
#include "listener.h"
#include "sound.h"
#include "scene.h"

namespace OpenAudio
{
	class OPENAUDIO_EXPORT AudioManager : public CoreBase::Base
	{
	public:
		static void Instantiate(CoreBase::Scene* scene,const QString& name = "audiomanager",
			ALCdevice* dev = NULL, ALCcontext* cntxt = NULL, bool shutdownPassedInContexts = true);
		void Destroy();
		ALCdevice* getDevice() { return _device; }
		ALCcontext* getContext() { return _context; }
		void setOpenALDevice(const ALCchar* deviceName);
		static AudioManager& GetInstance();
		static Listener* GetListener();
		void setDistanceModel(ALenum dm);
		void setDopplerFactor(float f);
		void setSpeedOfSound(float s);
		static bool IsInitialized() { return _audioMgr != NULL; }
		Sound* newSound(bool addintomanager = true);
		void addIntoManager(Sound* sound);
		void freeSound(Sound* sound);
		enum AudioMode
		{
			Listen,
			Draw,
		};
		void switchAudioMode();
		void setAudioMode(AudioMode mode);
		AudioMode getAudioMode();
		void changeSceneMode(unsigned int mode);
		void soundFileChanged(Sound* sound);
	public slots:
		virtual void onMessage(MessageData* data);
	private:
		struct BufferData
		{
			ALuint       buf;
			const char*  file;
			ALboolean    loop;
			unsigned int use;
			ALenum       format;
			ALsizei      freq;
			ALsizei      size;
			BufferData()
				: buf(0L)
				, file("")
				, loop(AL_FALSE)
				, use(0L)
			{}
		};
		typedef std::map<std::string, BufferData*>					BufferMap;
		typedef std::vector<osg::ref_ptr<Sound>>					SoundVector;
		enum SoundState
		{
			PAUSED,
			PLAYING,
			STOPPED
		};
		typedef std::map<Sound*, SoundState>						SoundObjectStateMap;
		static osg::ref_ptr<AudioManager>							_audioMgr;
		static osg::ref_ptr<Listener>								_listener;
		static const char*											_EaxVer;
		static const char*											_EaxSet;
		static const char*											_EaxGet;
		AudioManager(CoreBase::Scene* scene,const QString& name = "audiomanager",
			ALCdevice* dev = NULL, ALCcontext* cntxt = NULL, bool shutdownPassedInContexts = true);
		virtual ~AudioManager();
		inline void preFrame(const double deltaFrameTime);
		inline bool configured() const;
		inline bool configEAX(bool eax);
		inline int loadSoundBuffer(Sound& snd);
		inline int unloadSound(Sound* snd);
		inline bool releaseSoundSource(Sound& snd, const std::string& errorMessage,
			const std::string& callerFunctionName, int callerFunctionLineNum );
		inline bool releaseSoundBuffer(ALuint bufferHandle, const std::string& errorMessage,
			const std::string& callerFunctionName, int callerFunctionLineNum );
		void openDevice(const ALCchar* deviceName = 0);
		ALint loadFile(const std::string& file);
		bool unloadFile(const std::string& file);
		void createContext();
		void closeDevice();
		void closeContext();
		ALvoid*														_EAXSet;
		ALvoid*														_EAXGet;
		unsigned int												_numSounds;
		bool														_isConfigured;
		BufferMap													_bufferMap;
		SoundVector													_soundList;
		SoundObjectStateMap											_soundStateMap;
		ALCdevice*													_device;
		ALCcontext*													_context;
		bool														_shutdownContexts;
		CoreBase::Scene*											_mainScene;
		AudioMode													_audioMode;

	};
}